package de.ifgi;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

public class OilMain extends Activity implements OnClickListener, Runnable, SensorEventListener {
	
	private static final int REQUEST_BT_DISCOVERABLE = 0;
	private static final String tag = "OIL";
//    private TextView txtInfo;
	private Button btnStart;
	private Button btnStop;
	private Button btnRoute;
	private Bitmap floorMap;
	private ImageView imgView;
	private BluetoothAdapter btAdapter;
	private IntentFilter fNodesReady;
	private IntentFilter fEventsReady;
	private DataReceiver dataReceiver;
	private Bitmap fmOverlay;
	private BtHandler btHandler;
	private Canvas canvas;
	private Transformation tr;
	private ProgressDialog pd;
	private Point userPos;
	private Point exitPos;
	public EventsList eventsList;
	private Button btnLocalizeMe1;
	private Button btnLocalizeMe2;
	private Button btnLeastSquare;
//	private SensorManager sm;
//	private Sensor sOrientation;
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

//		txtInfo = (TextView)findViewById(R.id.txtInfo);

		btnStart = (Button)findViewById(R.id.btnStart);
		btnStart.setOnClickListener(this);
		
		btnStop = (Button)findViewById(R.id.btnStop);
		btnStop.setOnClickListener(this);
		
		btnRoute = (Button)findViewById(R.id.btnRoute);
		btnRoute.setOnClickListener(this);
		
		btnLocalizeMe1 = (Button)findViewById(R.id.btnLoc1);
		btnLocalizeMe1.setOnClickListener(this);
		
		btnLocalizeMe2 = (Button)findViewById(R.id.btnLoc2);
		btnLocalizeMe2.setOnClickListener(this);
		
		btnLeastSquare = (Button)findViewById(R.id.btnLsqr);
		btnLeastSquare.setOnClickListener(this);
		
		floorMap = BitmapFactory.decodeResource(getResources(), R.drawable.ifgi_5th_floor);
        imgView = (ImageView)findViewById(R.id.imgView);
        
        fmOverlay = Bitmap.createBitmap(floorMap.getWidth(), floorMap.getHeight(), Bitmap.Config.ARGB_4444);
        canvas = new Canvas(fmOverlay);
        canvas.drawBitmap(floorMap, 0, 0, null);
        imgView.setImageBitmap(fmOverlay);
        
        fNodesReady = new IntentFilter("de.ifgi.oil.action.NODES_READY");
        fEventsReady = new IntentFilter("de.ifgi.oil.action.EVENTS_READY");
        dataReceiver = new DataReceiver();

		btAdapter = BluetoothAdapter.getDefaultAdapter();
		btHandler = new BtHandler();
		
        double mapWidth = floorMap.getWidth();
        double mapHeight = floorMap.getHeight();
        Point pt1 = new Point(404449.711, 5755399.643);
        Point pt2 = new Point(404476.670, 5755385.608);
        tr = new Transformation(pt1, pt2, mapWidth, mapHeight);
        
        exitPos = new Point(404467.809, 5755418.527);
        
//        sm = (SensorManager) getSystemService(SENSOR_SERVICE);
//        sOrientation = sm.getDefaultSensor(Sensor.TYPE_ORIENTATION);
	}
	
	@Override
	public void onStart() {
		super.onStart();
		enableBt();
		setBtDiscoverable();
	}
	
	public void enableBt() {
		if (!btAdapter.isEnabled())
			btAdapter.enable();
	}
	
	private void setBtDiscoverable() {
		Intent intentBtDisc = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
		intentBtDisc.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
		startActivityForResult(intentBtDisc, REQUEST_BT_DISCOVERABLE);
	}
	
	protected void onActivityResult (int requestCode, int resultCode, Intent data) {
		if (requestCode == REQUEST_BT_DISCOVERABLE && resultCode == RESULT_CANCELED)
			Toast.makeText(this, "It is recommended to set bluetooth discoverable", Toast.LENGTH_SHORT).show();
	}
    
    // called when activity is in foreground and accessible to the user
    @Override
    public void onResume() {
    	super.onResume();
    	registerReceiver(dataReceiver, fNodesReady);
    	registerReceiver(dataReceiver, fEventsReady);
//    	sm.registerListener(this, sOrientation, SensorManager.SENSOR_DELAY_NORMAL);
    }
    
    // called when user moves to another activity
    @Override
    public void onPause() {
    	super.onPause();
    	unregisterReceiver(dataReceiver);
//    	sm.unregisterListener(this, sOrientation);
    }
    
    @Override
    public void onStop() {
    	super.onStop();
		disableBt();
	}
	
	public void disableBt() {
		if (btAdapter.isEnabled())
			btAdapter.disable();
	}
    
    @Override
    public void onDestroy() {
    	super.onDestroy();
    }

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onSensorChanged(SensorEvent event) {
		if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {
			float heading = event.values[0]; // shows where the device/user is heading
		}		
	}

    // not recommended?? should use this instead: onRetainNonConfigurationInstance()
    // and getLastNonConfigurationInstance()
    // or onSaveInstanceState() and onRestoreInstanceState() --> saves data only in Bundle
	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
	}
	
	private void drawBackground() {
        canvas.drawColor(0, Mode.CLEAR);
        fmOverlay = Bitmap.createBitmap(floorMap.getWidth(), floorMap.getHeight(), Bitmap.Config.ARGB_4444);
        canvas = new Canvas(fmOverlay);
        canvas.drawBitmap(floorMap, 0, 0, null);
	}
	
	private void displayUserPos(Point userPos) {
		Point pxUserPos = tr.transformPixel(userPos);
//        Log.d("OIL", "onDisplay="+pxUserPos.getX()+","+pxUserPos.getY());
        drawBackground();
        Bitmap marker = BitmapFactory.decodeResource(getResources(), R.drawable.ic_yah);
        canvas.drawBitmap(marker, (float)(pxUserPos.getX()-(marker.getWidth()/2)), 
        		(float)(pxUserPos.getY()-(marker.getHeight()/2)), null);
        imgView.setImageBitmap(fmOverlay);
	}
	
	private void displayEventPos(List<Event> eventsList) {
		for (Event e : eventsList) {
			Point pxEventPos = tr.transformPixel(e.getEstPosition());
			Log.d("OIL", pxEventPos.getX()+","+pxEventPos.getY());
			Bitmap marker = BitmapFactory.decodeResource(getResources(), R.drawable.ic_danger);
	        canvas.drawBitmap(marker, (float)(pxEventPos.getX()-(marker.getWidth()/2)), 
	        		(float)(pxEventPos.getY()-(marker.getHeight()/2)), null);
		}
		imgView.setImageBitmap(fmOverlay);
	}
	
	private void displayRoute() {		
		if (userPos != null) {
			Routing r = new Routing(this);
			List<Point> route = r.generateRoute(userPos, exitPos);
			
			List<Point> pxRoute = new ArrayList<Point>();
	        for (Point p : route) {
				Point px = tr.transformPixel(p);
				pxRoute.add(px);
			}
	        
	        Paint paint = new Paint();
	        paint.setColor(Color.RED);
	        paint.setStyle(Paint.Style.FILL);
	        paint.setStrokeWidth(3);
	        paint.setFlags(Paint.ANTI_ALIAS_FLAG);
			
	        for (int i=0; i<pxRoute.size()-1; i++) {
	        	canvas.drawLine((float)pxRoute.get(i).getX(), (float)pxRoute.get(i).getY(), 
	        			(float)pxRoute.get(i+1).getX(), (float)pxRoute.get(i+1).getY(), paint);
	        }
	        imgView.setImageBitmap(fmOverlay);
	        
		} else {
			Toast.makeText(this, "Press Start to calculate your position, " +
					"then press Show Route to see the routes.", Toast.LENGTH_LONG).show();
		}
	}
    
	private void setBtFullName(Point userPos) {
		String btFullName = btAdapter.getName();
		String macAddName;
		if (btFullName.startsWith("Node")) {
			macAddName = btFullName.split("/")[0].split("_")[1];
		} else {
			macAddName = btAdapter.getAddress();
		}
		
		String strBtName = "Node_" + macAddName + "/" + userPos.getX() + "," + userPos.getY();
		btAdapter.setName(strBtName);
		
		// IMPORTANT: after setting new name, 
		// Bluetooth has to be restarted so that name change is recognized
		// BUT this affects the next intent broadcast...
		// btAdapter is not enabled???
//		disableBt();
//		enableBt();
	}

	@Override
	public void onClick(View v) {
		if (v.getId() == R.id.btnStart) {
			startDiscNodes();
//			pd = ProgressDialog.show(this, "", "Calculating...");
//			Thread t = new Thread(this);
//			t.start();
		} else if (v.getId() == R.id.btnStop) {
			stopDiscNodes();
		} else if (v.getId() == R.id.btnRoute) {
			displayRoute();
		} else if (v.getId() == R.id.btnLoc1) {
			setUpData();
			localizeMe1();
		} else if (v.getId() == R.id.btnLoc2) {
			setUpData();
			localizeMe2();
		} else if (v.getId() == R.id.btnLsqr) {
			setUpData();
			leastSquare();
		}
	}
	
	private void setUpData() {
		NodesList nodesList = NodesList.getNodesList();
		nodesList.clear();
		Node n1 = new Node("Node1", "-48", new Point(404484.52,5755415.818));
		Node n2 = new Node("Node2", "-53", new Point(404480.238,5755415.818));
		Node n3 = new Node("Node3", "-48", new Point(404482.775,5755412.25));

//		Node n1 = new Node("Node1", "-41", new Point(404484.52,5755415.818));
//		Node n2 = new Node("Node2", "-53", new Point(404480.238,5755415.818));
//		Node n3 = new Node("Node3", "-54", new Point(404482.775,5755412.25));

//		Node n1 = new Node("Node1", "-42", new Point(404484.52,5755415.818));
//		Node n2 = new Node("Node2", "-51", new Point(404480.238,5755415.818));
//		Node n3 = new Node("Node3", "-57", new Point(404482.775,5755412.25));


//		Node n1 = new Node("Node1", "-67", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-62", new Point(404482.775,5755412.25));
//		Node n3 = new Node("Node3", "-56", new Point(404478.256,5755407.651));

//		Node n1 = new Node("Node1", "-61", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-61", new Point(404482.775,5755412.25));
//		Node n3 = new Node("Node3", "-55", new Point(404478.256,5755407.651));
//
//		Node n1 = new Node("Node1", "-64", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-59", new Point(404482.775,5755412.25));
//		Node n3 = new Node("Node3", "-53", new Point(404478.256,5755407.651));
//
//		F3, F4, F5 --> R3
//		Node n1 = new Node("Node1", "-69", new Point(404482.775,5755412.25));
//		Node n2 = new Node("Node2", "-57", new Point(404478.256,5755407.651));
//		Node n3 = new Node("Node3", "-61", new Point(404469.137,5755411.933));
//
//		Node n1 = new Node("Node1", "-67", new Point(404482.775,5755412.25));
//		Node n2 = new Node("Node2", "-57", new Point(404478.256,5755407.651));
//		Node n3 = new Node("Node3", "-69", new Point(404469.137,5755411.933));
//
//		Node n1 = new Node("Node1", "-63", new Point(404482.775,5755412.25));
//		Node n2 = new Node("Node2", "-57", new Point(404478.256,5755407.651));
//		Node n3 = new Node("Node3", "-56", new Point(404469.137,5755411.933));
//
//		F5, F6, F7 --> R4
//		Node n1 = new Node("Node1", "-63", new Point(404469.137,5755411.933));
//		Node n2 = new Node("Node2", "-56", new Point(404468.427,5755416.906));
//		Node n3 = new Node("Node3", "-60", new Point(404474.53,5755419.002));
//
//		Node n1 = new Node("Node1", "-67", new Point(404469.137,5755411.933));
//		Node n2 = new Node("Node2", "-63", new Point(404468.427,5755416.906));
//		Node n3 = new Node("Node3", "-64", new Point(404474.53,5755419.002));
//
//		Node n1 = new Node("Node1", "-66", new Point(404469.137,5755411.933));
//		Node n2 = new Node("Node2", "-58", new Point(404468.427,5755416.906));
//		Node n3 = new Node("Node3", "-63", new Point(404474.53,5755419.002));
//
//		F1, F4, F5 --> R5
//		Node n1 = new Node("Node1", "-80", new Point(404484.52,5755415.818));
//		Node n2 = new Node("Node2", "-59", new Point(404478.256,5755407.651));
//		Node n3 = new Node("Node3", "-57", new Point(404469.137,5755411.933));
//
//		Node n1 = new Node("Node1", "-76", new Point(404484.52,5755415.818));
//		Node n2 = new Node("Node2", "-57", new Point(404478.256,5755407.651));
//		Node n3 = new Node("Node3", "-65", new Point(404469.137,5755411.933));
//
//		Node n1 = new Node("Node1", "-70", new Point(404484.52,5755415.818));
//		Node n2 = new Node("Node2", "-62", new Point(404478.256,5755407.651));
//		Node n3 = new Node("Node3", "-57", new Point(404469.137,5755411.933));
//
//		F2, F4, F5 --> R6
//		Node n1 = new Node("Node1", "-68", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-59", new Point(404478.256,5755407.651));
//		Node n3 = new Node("Node3", "-62", new Point(404469.137,5755411.933));
//
//		Node n1 = new Node("Node1", "-70", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-60", new Point(404478.256,5755407.651));
//		Node n3 = new Node("Node3", "-55", new Point(404469.137,5755411.933));
//
//		Node n1 = new Node("Node1", "-72", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-58", new Point(404478.256,5755407.651));
//		Node n3 = new Node("Node3", "-59", new Point(404469.137,5755411.933));
//
//		F2, F5, F6 --> R7
//		Node n1 = new Node("Node1", "-69", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-63", new Point(404469.137,5755411.933));
//		Node n3 = new Node("Node3", "-62", new Point(404468.427,5755416.906));
//
//		Node n1 = new Node("Node1", "-76", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-59", new Point(404469.137,5755411.933));
//		Node n3 = new Node("Node3", "-60", new Point(404468.427,5755416.906));
//
//		Node n1 = new Node("Node1", "-78", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-68", new Point(404469.137,5755411.933));
//		Node n3 = new Node("Node3", "-57", new Point(404468.427,5755416.906));
//
//		F2, F6, F7 --> R8
//		Node n1 = new Node("Node1", "-63", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-64", new Point(404468.427,5755416.906));
//		Node n3 = new Node("Node3", "-34", new Point(404474.53,5755419.002));
//
//		Node n1 = new Node("Node1", "-77", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-63", new Point(404468.427,5755416.906));
//		Node n3 = new Node("Node3", "-33", new Point(404474.53,5755419.002));
//
//		Node n1 = new Node("Node1", "-72", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-62", new Point(404468.427,5755416.906));
//		Node n3 = new Node("Node3", "-36", new Point(404474.53,5755419.002));
//
//		F2, F7, F8 --> R9
//		Node n1 = new Node("Node1", "-59", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-63", new Point(404474.53,5755419.002));
//		Node n3 = new Node("Node3", "-62", new Point(404479.279,5755418.613));
//
//		Node n1 = new Node("Node1", "-80", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-61", new Point(404474.53,5755419.002));
//		Node n3 = new Node("Node3", "-61", new Point(404479.279,5755418.613));
//
//		Node n1 = new Node("Node1", "-66", new Point(404480.238,5755415.818));
//		Node n2 = new Node("Node2", "-69", new Point(404474.53,5755419.002));
//		Node n3 = new Node("Node3", "-54", new Point(404479.279,5755418.613));
//
//		F3, F7, F8 --> R10
//		Node n1 = new Node("Node1", "-62", new Point(404482.775,5755412.25));
//		Node n2 = new Node("Node2", "-71", new Point(404474.53,5755419.002));
//		Node n3 = new Node("Node3", "-58", new Point(404479.279,5755418.613));
//
//		Node n1 = new Node("Node1", "-70", new Point(404482.775,5755412.25));
//		Node n2 = new Node("Node2", "-73", new Point(404474.53,5755419.002));
//		Node n3 = new Node("Node3", "-54", new Point(404479.279,5755418.613));
//
//		Node n1 = new Node("Node1", "-65", new Point(404482.775,5755412.25));
//		Node n2 = new Node("Node2", "-60", new Point(404474.53,5755419.002));
//		Node n3 = new Node("Node3", "-56", new Point(404479.279,5755418.613));
				
		nodesList.add(n1);
		nodesList.add(n2);
		nodesList.add(n3);
	}
	
	private void localizeMe1() {
		Intent serviceIntent = new Intent(this, Localization.class);
    	serviceIntent.putExtra("action", "localizeMe1");
    	this.startService(serviceIntent);
	}
	
	private void localizeMe2() {	
		Intent serviceIntent = new Intent(this, Localization.class);
    	serviceIntent.putExtra("action", "localizeMe2");
    	this.startService(serviceIntent);
	}
	
	private void leastSquare() {
		Intent serviceIntent = new Intent(this, Localization.class);
    	serviceIntent.putExtra("action", "leastSquare");
    	this.startService(serviceIntent);
	}

	public void run() {
		startDiscNodes();
	}

	public void startDiscNodes() {
		Log.d(tag, "start discovery");
		btHandler.sleep(15000);
		if (!btAdapter.isDiscovering())
			btAdapter.startDiscovery();
	}

	public void stopDiscNodes() {
		Log.d(tag, "stop discovery");
		if (btAdapter.isDiscovering())
			btAdapter.cancelDiscovery();
		btHandler.sendEmptyMessage(1);
	}
	
	class BtHandler extends Handler {
    	@Override
		public void handleMessage(Message msg) {
			switch(msg.what) {
			case 0:
				Log.d(tag, "restart discovery");
				this.removeMessages(0);
				startDiscNodes();
				break;
			case 5:
				pd.dismiss();
			default:
				this.removeMessages(0);
				break;
			}
		}
    	
		public void sleep(long delayMillis) {
			this.removeMessages(0);
			sendMessageDelayed(obtainMessage(0), delayMillis);
		}
	}
	
	class DataReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			String strIntent = intent.getAction();
			if (strIntent.equalsIgnoreCase("de.ifgi.oil.action.NODES_READY")) {
				// dismiss the progress dialog
				btHandler.sendEmptyMessage(5);
				userPos = intent.getParcelableExtra("userPos");
				Log.d("OIL", "Final position="+userPos.getX()+","+userPos.getY());
				displayUserPos(userPos);
				setBtFullName(userPos);
			} else if (strIntent.equalsIgnoreCase("de.ifgi.oil.action.EVENTS_READY")) {
				eventsList = EventsList.getEventsList();
				displayEventPos(eventsList);
			}
			
		}
		
	}
	
}
